#region Namespace

using DSO;
using ADODB;
using System;
using System.IO;
using System.Data;
using System.Data.OleDb;
using System.Diagnostics;
using System.Collections;
using System.Data.SqlClient;
using System.DirectoryServices;
using System.Runtime.InteropServices;
using Microsoft.CommerceServer.Runtime;
using Microsoft.CommerceServer.Interop;
using Microsoft.CommerceServer.Interop.Configuration;
using Microsoft.CommerceServer.Runtime.Configuration;

#endregion Namespace

namespace DeleteCS2K2Site
{

	#region Class DeleteSite

	/// <summary>
	/// This class is designed to delete a CS2K2 site.
	/// </summary>
	class DeleteSite
	{
		#region Private Variables

		/// <summary>
		/// ArrayList to hold all Commerce sites
		/// </summary>
		private static ArrayList CSSites;
		/// <summary>
		/// ArrayList to hold all Connection string to a Commerce site
		/// </summary>
		private ArrayList connectionsStr;
		/// <summary>
		/// ArrayList to hold all Commerce site's Databases
		/// </summary>
		private ArrayList databases;
		/// <summary>
		/// Commerce site naeme
		/// </summary>
		private string sitename;
		/// <summary>
		/// Global Config object
		/// </summary>
		private GlobalConfig oGlbCfg = new GlobalConfigClass();
		/// <summary>
		/// Site Config readonly object
		/// </summary>
		private SiteConfigReadOnly SiteCfg = new SiteConfigReadOnlyClass();
		/// <summary>
		/// Site Config object
		/// </summary>
		private SiteConfig siteConfig = new SiteConfigClass();
		
		#endregion Private Variables

		#region Main

		/// <summary>
		/// The main entry point for the DeleteCS2K2SIte. 
		/// If a site name is passed do a silent removal 
		/// otherwise display site available for selection.
		/// </summary>
		/// <param name="args">Commerce Server sitename</param>
		/// <example>
		/// <code lang="C#">DeleteCS2K2Site.exe sitename</code>
		/// </example>
		[STAThread]
		static void Main(string[] args)
		{
			// users selection for a silent removal
			string selection = null;

			// did we get any arguments?
			if(args.Length == 0)
			{
				Displaysites();
				selection = Console.ReadLine();
				
				while (selection != "")
				{
					try
					{
						int arrayPos = Convert.ToInt16(selection);
						selection = "";
						Console.WriteLine();
						Console.WriteLine("Deleting "+CSSites[arrayPos].ToString()+"...");

						try
						{
							DeleteSite dsite = new DeleteSite(CSSites[arrayPos].ToString());
							dsite.RemoveCSSite();
						}
						catch(Exception e)
						{
							Console.WriteLine(e.Message);
						}
					}
					catch(System.FormatException)
					{
						Console.WriteLine("\n("+selection+") does not correspond to a Commerce Site\n");
						Displaysites();
						selection = Console.ReadLine();
					}
					catch(Exception e)
					{
						Console.WriteLine(e.Message);
					}
				}
			}
			else
			{
				selection = args[0].ToString();
				if(selection!=null)
				{
					try
					{
						DeleteSite dsite = new DeleteSite(selection);
						dsite.RemoveCSSite();
					}
					catch(Exception e)
					{
						Console.WriteLine(e.Message);
					}
				}
			}
		}// end main

		#endregion Main

		#region Private Methods

		/// <summary>
		/// Display all Commerce Site and allow user to select a single site.
		/// </summary>
		/// <example>
		/// <note type="note">This is a static method...</note>
		/// <code lang="C#">Displaysites();</code>
		/// </example>
		private static void Displaysites()
		{
			CSSites = new ArrayList();
			// If we didn't get a site name then get all Commerce Sites
			GlobalConfig oGlbCfg = new GlobalConfigClass();
			oGlbCfg.Initialize("");

			// Get all sites
			Recordset rst = oGlbCfg.Sites();
			int i = 0;

			// Dispaly a list of sites
			foreach(Field fld in rst.Fields)
			{
				i = CSSites.Add(fld.Value.ToString());
				Console.WriteLine("("+i.ToString() +") "+ fld.Value.ToString());
			}
			Console.Write("Select a number for corresponding Commerce Site (or enter to quit): ");
		}// end Displaysites

		/// <summary>
		/// Get all Database Connection string for the selected Commerce site
		/// </summary>
		/// <example>
		/// <code lang="C#">GetConnectionStrings();</code>
		/// </example>
		private ArrayList GetConnectionStrings()
		{
			ArrayList siteConStrs = new ArrayList();
			string curfldName = null;
			string fldName = null;
			
			foreach(Field fld in SiteCfg.Fields)
			{
				// ignore dupe names
				fldName = fld.Name;
				Recordset rst = fld.Value as Recordset;
				// if we got a recordset then find out it we have a database
				if(rst!=null)
				{
					foreach(Field f in rst.Fields)
					{
						// determin if we have a connection string
						if(f.Name.ToString().IndexOf("connstr_db", 0)==0 || f.Name.ToString().IndexOf("s_BizDataStoreConnectionString")==0)
						{
							string dbConStr = f.Value.ToString();
							// strip out server name and database name from connection string
							string dbName;
							string dbServer;
							GetDatabase(dbConStr, out dbName, out dbServer);
							if(dbName!="")
							{
								// format all connection string to default
								dbConStr = "Provider=SQLOLEDB.1;Integrated Security=SSPI;Persist Security Info=True;Initial Catalog="+dbName+";Data Source="+dbServer+";";
								// search the array for the database name
								if(siteConStrs.BinarySearch(dbConStr, Comparer.Default) < 0 && dbConStr !="")
								{
									// the datawarehouse resource has two connection string we only need one
									// this works for any resource that may have two connection string
									if(curfldName != fldName)
									{
										siteConStrs.Add(dbConStr);
										// do a sort so our search will work
										siteConStrs.Sort();
										curfldName = fldName;
									}
								}
							}
						}
					}
				}
			}
			Console.WriteLine("Retrieved all connection strings.");
			return siteConStrs;
		}// end GetConnectionStrings

		/// <summary>
		/// Strip out Server Name and Database Name from Connection string
		/// </summary>
		/// <param name="ConStr">An OleDb Connection string</param>
		/// <param name="dbName">Database Name</param>
		/// <param name="dbServer">Server Name</param>
		/// <example>
		/// <code lang="C#">
		/// string dbName;
		/// string dbServer;
		/// GetDatabase(dbConStr, out dbName, out dbServer);
		/// </code>
		/// </example>
		private void GetDatabase(string ConStr, out string dbName, out string dbServer)
		{
			// create a connection
			OleDbConnection Con = new OleDbConnection();

			// get the database name
			Con.ConnectionString = ConStr;
			dbName = Con.Database;
			dbServer = Con.DataSource;
		}// end GetDatabase

		/// <summary>
		/// Drop databases...
		/// <note type="note">Before dropping the databases we will kill all active connection...</note>
		/// </summary>
		/// <param name="dbName">Database Name</param>
		/// <param name="dbServer">Server Name</param>
		/// <example>
		/// <code lang="C#">DropCSDatabases(dbServer, dbName);</code>
		/// </example>
		private void DropCSDatabases(string dbServer, string dbName)
		{
			// don't delete predictor and direct mailer databases
			if(dbName!="DirectMailer" && dbName!="Predictor")
			{
				SqlConnection Con = null;
				SqlCommand Cmd = null;
				string SQLCmd = "DROP DATABASE " + dbName;

				Con = new SqlConnection("Data Source=" + dbServer + ";Initial Catalog=master;Integrated Security=SSPI");
				Con.Open();
			
				// kill all active connections
				KilActivelDBConnections(dbServer, dbName);

				try 
				{
					Cmd = new SqlCommand(SQLCmd, Con);
					Cmd.CommandType = CommandType.Text;
					Cmd.ExecuteReader();
					Con.Close();
					Console.WriteLine("\nDeleted the database " + dbName + ".");
				}
				catch (Exception e) 
				{
					Console.WriteLine(e.Message);
				}
			}
		}// end DropCSDatabases

		/// <summary>
		/// Delete Commerce Applications
		/// </summary>
		/// <example>
		///<code lang="C#">DeleteApplications();</code>
		///</example>
		private void DeleteApplications()
		{
			try
			{
				Console.WriteLine("\nDeleting the site\'s Applications...\n");
				
				foreach(Field fld in siteConfig.Fields)
				{
					// only get recordsets
					Recordset rst = fld.Value as Recordset;
					if(rst!=null)
					{
						// only deal with Applications
						if(rst.Fields["s_ResourceType"].Value.ToString() == "Address")
						{
							System.Array WebServerMachine = (System.Array)siteConfig.MakeArrayFromString(rst.Fields["s_WebServerMachine"].Value.ToString());
							System.Array WebServerInstance = (System.Array)siteConfig.MakeArrayFromString(rst.Fields["s_WebServerInstance"].Value.ToString());
							string VirtualRootName = rst.Fields["s_VirtualRootName"].Value.ToString();

							DeleteWebSite(WebServerMachine.GetValue(0).ToString(), WebServerInstance.GetValue(0).ToString(), VirtualRootName);
						}
					}
				}
			}
			catch (Exception e)
			{
				Console.WriteLine("Unable to delete Application: "+ e.Message);
			}
		}// end DeleteApplications

		/// <summary>
		/// Delete Commerce Web site
		/// </summary>
		/// <param name="Server">Server Name</param>
		/// <param name="IISInstance">IIS instance hosting Web</param>
		/// <param name="sPath">IIS Web application path</param>
		/// <example>
		/// <code lang="C#">DeleteWebSite(Server, IISInstance, sPath);</code>
		/// </example>
		private void DeleteWebSite(string Server, string IISInstance, string sPath)
		{
			bool Root = false;
			string sIISPath = @"IIS:\\" + Server + @"\W3SVC\" + IISInstance + @"\Root";

			object[] oParams = new object[2] {"IISWebVirtualDir", sPath};
			if(sPath==@"/")
			{
				sPath = "";
				Root = true;
			}
			else
			{
				sPath = @"\"+sPath;
			}

			// reset IIS before deleting
			IISReset(Server);
	
			DirectoryEntry oDE = new DirectoryEntry(sIISPath);
			DirectoryInfo dir = new DirectoryInfo(oDE.Properties["Path"].Value.ToString()+sPath);

			if(Root)
			{
				Console.WriteLine("Directory is root of IIS you must manually delete all files and folders...");
			}
			else
			{
				// delete the IIS directroy
				oDE.Invoke("Delete", oParams);

				// delete the folder
				dir.Delete(true);
				Console.WriteLine("deleting ("+oDE.Properties["Path"].Value.ToString()+sPath+") directory and it's files");
			}
				
		}// end DeleteWebSite

		/// <summary>
		/// Delete local Commerce resources
		/// </summary>
		/// <example>
		/// <code lang="C#">DeleteLocalResources();</code>
		/// </example>
		private void DeleteLocalResources()
		{
			try
			{
				Console.WriteLine("\nDeleting the site\'s local resources...\n");

				foreach(Field fld in siteConfig.Fields)
				{
					if(fld.Value.GetType().IsCOMObject)
					{
						try
						{
							siteConfig.DeleteComponentConfig(fld.Name);
							Console.WriteLine(String.Concat("Deleted resource ", fld.Name));
						}
						catch
						{
						}
					}
				}
			}
			catch (Exception e)
			{
				Console.WriteLine("Unable to delete local resources: "+ e.Message);
			}		
		}// end DeleteLocalResources

		/// <summary>
		/// Delete global Commerce resources
		/// </summary>
		/// <example>
		/// <code lang="C#">DeleteGlobalResources();</code>
		/// </example>
		private void DeleteGlobalResources()
		{
			try
			{
				Console.WriteLine("\nDeleting the site\'s global resources...\n");

				foreach(Field fld in SiteCfg.Fields)
				{
					if(fld.Value.GetType().IsCOMObject)
					{
						if (((bool)((Recordset)SiteCfg.Fields[fld.Name].Value).Fields["f_IsGlobal"].Value))
						{
							try
							{
								// don't delete "Predictor" and "Direct Mail" global resources
								if(fld.Name != "Predictor" && fld.Name != "Direct Mail")
								{
									oGlbCfg.DeleteServiceConfig(((Recordset)SiteCfg.Fields[fld.Name].Value).Fields["s_RefResource"].Value.ToString());
									Console.WriteLine(String.Concat("Deleted the global resource ", ((Recordset)SiteCfg.Fields[fld.Name].Value).Fields["s_RefResource"].Value.ToString()));
								}
							}
							catch{}
						}// end if f_IsGlobal
					}// end if IsCOMObject
				}// end foreach
			}
			catch (Exception e)
			{
				Console.WriteLine("Unable to delete global resources: "+ e.Message);
			}		
		}// end DeleteGlobalResources

		/// <summary>
		/// Delete Commerce Site
		/// </summary>
		/// <example>
		/// <code lang="C#">DeleteCSSite();</code>
		/// </example>
		private void DeleteCSSite()
		{
			try
			{
				Console.WriteLine("Deleting the site...\n");
				oGlbCfg.DeleteSiteConfig(sitename);
				Console.WriteLine("Deleted the site.");
			}
			catch (Exception e)
			{
				Console.WriteLine("Unable to delete the site: "+ e.Message);
			}
		}// end DeleteCSSite

		/// <summary>
		/// Reset IIS...
		/// </summary>
		/// <note type="note">kill all active connection...</note>
		/// <param name="Server">Server Name</param>
		/// <example>
		///<code lang="C#">
		///IISReset(Server);
		///</code>
		///</example>
		private void IISReset(string Server)
		{
			Process IISProcess;
			string iisCmd = "/C iisreset " + Server;
			ProcessStartInfo startinfo = new ProcessStartInfo("cmd.exe");

			startinfo.Arguments = iisCmd;
			startinfo.RedirectStandardOutput = true;
			startinfo.UseShellExecute = false;

			IISProcess = Process.Start(startinfo);

			Console.Write(IISProcess.StandardOutput.ReadToEnd());			
			
			IISProcess.WaitForExit();
		}// end IISReset

		/// <summary>
		/// kill all active connection...
		/// </summary>
		/// <param name="datasource">Server Name</param>
		/// <param name="DbName">Database Name</param>
		/// <example>
		/// <code lang="C#">KilActivelDBConnections(datasource, DbName);</code>
		/// </example>
		private void KilActivelDBConnections(string datasource, string DbName)
		{
			SqlConnection Con = null;
			SqlCommand Cmd = null;
			SqlDataReader DrSpid = null;
			SqlConnection Con2 = null;
			SqlDataReader DrKill = null;
			string SqlKill = "SELECT  spid FROM master.dbo.sysprocesses WHERE DB_NAME(dbid) = '" + DbName + "'";

			try 
			{
				Con  = new SqlConnection("Data Source=" + datasource + ";Initial Catalog=master;Integrated Security=SSPI");
				Con2 = new SqlConnection("Data Source=" + datasource + ";Initial Catalog=master;Integrated Security=SSPI");
				Con.Open();
				Con2.Open();
				Cmd = new SqlCommand(SqlKill, Con);
				Cmd.CommandType = CommandType.Text;
				DrSpid = Cmd.ExecuteReader();
				while (DrSpid.Read()) 
				{
					try 
					{
						Cmd = new SqlCommand("kill " + DrSpid.GetValue(0), Con2);
						DrKill = Cmd.ExecuteReader();
					}
					catch (Exception e){Console.WriteLine(e.Message);}
					finally {DrKill.Close();}
				}
			}
			catch (Exception e){Console.WriteLine(e.Message);}
			finally{DrSpid.Close();}
			Console.WriteLine("killed all active connection to Data Source="+ datasource +" Initial Catalog="+DbName);
		}// end KilActivelDBConnections

		/// <summary>
		/// Delete Data Warehouse
		/// </summary>
		/// <example>
		/// <code lang="C#">DeleteDW();</code>
		/// </example>
		private void DeleteDW()
		{
			// create a (DW) DSO server and database (COM Objects) to work with
			DSO.Server dsoServer = new DSO.ServerClass();
			DSO.Database dsoDB = new DSO.DatabaseClass();

			try
			{
				foreach(Field fld in SiteCfg.Fields)
				{
					// only get recordsets
					Recordset rst = fld.Value as Recordset;
					if(rst!=null)
					{
						// only work with global resources
						if(((bool)rst.Fields["f_IsGlobal"].Value) && rst.Fields["s_ResourceType"].Value.ToString() == "DataWarehouse_Group")
						{
							// get the database and server
							string dbName = ((Recordset)SiteCfg.Fields["Global Data Warehouse"].Value).Fields["Schema_ctlgDbName"].Value.ToString();
							string dbServer = ((Recordset)SiteCfg.Fields["Global Data Warehouse"].Value).Fields["Schema_ctlgOLAPServerName"].Value.ToString();

							// Create a connection to the Analysis server.
							dsoServer.Connect(dbServer);

							// Check to make sure that the database is on
							// the Analysis server.
							if(dsoServer.MDStores.Find(dbName))
							{
								// The database was on the server.
								// Delete the database.
								dsoServer.MDStores.Remove(dbName);
								Console.WriteLine("Deleted Data Warehouse...\n");
								break;
							}
						}
					}
				}
			}
			catch (Exception e) {Console.WriteLine(e.Message);}
			finally
			{
				// release COM objects
				ReleaseCOMObject(dsoServer);
				ReleaseCOMObject(dsoDB);
			}
		}// end DeleteDW

		/// <summary>
		/// Release COM Object
		/// </summary>
		/// <example>
		/// <code lang="C#">ReleaseCOMObject(COMObject);</code>
		/// </example>
		private static void ReleaseCOMObject(object COMObject)
		{
			if (COMObject != null) 
			{
				Marshal.ReleaseComObject(COMObject);
				GC.SuppressFinalize(COMObject);
			}
			COMObject = null;
		}// end ReleaseCOMObject
	
		#endregion Private Methods

		#region Public Methods

		/// <summary>
		/// Constructor for DeleteSite.
		/// </summary>
		/// <param name="args">Commerce Server sitename</param>
		/// <example>
		/// <code lang="C#">DeleteSite dsite = new DeleteSite(siteName);</code>
		/// </example>
		public DeleteSite(string siteName)
		{
			// initialize private variables
			this.databases = new ArrayList();
			this.sitename = siteName;
			this.connectionsStr = new ArrayList();
			this.oGlbCfg.Initialize(null);
			this.SiteCfg.Initialize(this.sitename);
			this.siteConfig.Initialize(sitename);
		}// end DeleteSite

		/// <summary>
		/// Remove selected Commerce site
		/// </summary>
		/// <example>
		/// <code lang="C#">RemoveCSSite();</code>
		/// </example>
		public void RemoveCSSite()
		{
			// get all Connection strings
			ArrayList ConStr = GetConnectionStrings();

			// delete all CS databases
			foreach(string str in ConStr)
			{
				string dbName;
				string dbServer;
				GetDatabase(str, out dbName, out dbServer);
				DropCSDatabases(dbServer, dbName);
			}

			// delete all aplications
			DeleteApplications();

			// delete site resources
			DeleteGlobalResources();
			DeleteLocalResources();

			// delete DW
			DeleteDW();

			// delete CS site
			DeleteCSSite();

			// house cleaning
			ReleaseCOMObject(oGlbCfg);
			ReleaseCOMObject(SiteCfg);
			ReleaseCOMObject(siteConfig);
		}// end RemoveCSSite

		#endregion Public Methods

	}// end class DeleteSite

	#endregion Class DeleteSite

}// End namespace DeleteCS2K2Site
